home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 August: Tool Chest / Dev.CD Aug 95 TC / Dev.CD Aug 95 TC.toast / Sample Code / MoreFiles 1.3.1 / DirectoryCopy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-16  |  15.5 KB  |  453 lines  |  [TEXT/MMCC]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    DirectoryCopy: A robust, general purpose directory copy routine.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support
  7. **
  8. **    File:        DirectoryCopy.c
  9. **
  10. **    Copyright © 1992-1995 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Errors.h>
  24. #include <Memory.h>
  25. #include <Files.h>
  26. #include "MoreFiles.h"
  27. #include "MoreFilesExtras.h"
  28. #include "MoreDesktopMgr.h"
  29. #include "FileCopy.h"
  30. #include "DirectoryCopy.h"
  31.  
  32. /*****************************************************************************/
  33.  
  34. /* local constants */
  35.  
  36. enum
  37. {
  38.     dirCopyBigCopyBuffSize  = 0x00004000,
  39.     dirCopyMinCopyBuffSize  = 0x00000200
  40. };
  41.  
  42.  
  43. /*****************************************************************************/
  44.  
  45. /* local data structures */
  46.  
  47. /* The EnumerateGlobals structure is used to minimize the amount of
  48. ** stack space used when recursively calling CopyLevel and to hold
  49. ** global information that might be needed at any time. */
  50.  
  51. #if GENERATINGPOWERPC
  52. #pragma options align=mac68k
  53. #endif
  54. struct EnumerateGlobals
  55. {
  56.     Ptr            copyBuffer;            /* pointer to buffer used for file copy operations */
  57.     long        bufferSize;            /* the size of the copy buffer */
  58.     CopyErrProcPtr errorHandler;    /* pointer to error handling function */
  59.     OSErr        error;                /* temporary holder of results - saves 2 bytes of stack each level */
  60.     Boolean        bailout;            /* set to true to by error handling function if fatal error */
  61.     short        destinationVRefNum;    /* the destination vRefNum */
  62.     Str63        itemName;            /* the name of the current item */
  63.     CInfoPBRec    myCPB;                /* the parameter block used for PBGetCatInfo calls */
  64. };
  65. #if GENERATINGPOWERPC
  66. #pragma options align=reset
  67. #endif
  68.  
  69. typedef struct EnumerateGlobals EnumerateGlobals;
  70. typedef EnumerateGlobals *EnumerateGlobalsPtr;
  71.  
  72.  
  73. /* The PreflightGlobals structure is used to minimize the amount of
  74. ** stack space used when recursively calling GetLevelSize and to hold
  75. ** global information that might be needed at any time. */
  76.  
  77. #if GENERATINGPOWERPC
  78. #pragma options align=mac68k
  79. #endif
  80. struct PreflightGlobals
  81. {
  82.     OSErr            result;                /* temporary holder of results - saves 2 bytes of stack each level */
  83.     Str63            itemName;            /* the name of the current item */
  84.     CInfoPBRec        myCPB;                /* the parameter block used for PBGetCatInfo calls */
  85.  
  86.     unsigned short    dstBlksPerAllocBlk;    /* the number of 512 byte blocks per allocation block on destination */
  87.     unsigned short    allocBlksNeeded;    /* the total number of allocation blocks needed  */
  88.  
  89.     unsigned short    tempBlocks;            /* temporary storage for calculations (save some stack space)  */
  90. };
  91. #if GENERATINGPOWERPC
  92. #pragma options align=reset
  93. #endif
  94.  
  95. typedef struct PreflightGlobals PreflightGlobals;
  96. typedef PreflightGlobals *PreflightGlobalsPtr;
  97.  
  98. /*****************************************************************************/
  99.  
  100. /* static prototypes */
  101.  
  102. static    void    GetLevelSize(long currentDirID,
  103.                              PreflightGlobalsPtr theGlobals);
  104.  
  105. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  106.                                             long srcDirID,
  107.                                             short dstVRefNum,
  108.                                             Boolean *spaceOK);
  109.  
  110. static    void    CopyLevel(long sourceDirID,
  111.                           long dstDirID,
  112.                           EnumerateGlobalsPtr theGlobals);
  113.                           
  114. /*****************************************************************************/
  115.  
  116. static    void    GetLevelSize(long currentDirID,
  117.                              PreflightGlobalsPtr theGlobals)
  118. {
  119.     short    index = 1;
  120.     
  121.     do
  122.     {
  123.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  124.         theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID;    /* we need to do this every time */
  125.                                                             /* through, since GetCatInfo  */
  126.                                                             /* returns ioFlNum in this field */
  127.         theGlobals->result = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);
  128.         if ( theGlobals->result == noErr )
  129.         {
  130.             if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  131.             {
  132.                 /* we have a directory */
  133.                 
  134.                 GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
  135.                 theGlobals->result = noErr; /* clear error return on way back */
  136.             }
  137.             else
  138.             {
  139.                 /* we have a file - add its allocation blocks to allocBlksNeeded */
  140.                 
  141.                 /* get number of 512-byte blocks needed for data fork */
  142.                 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen % 512) ?
  143.                                             (((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1) :
  144.                                             ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9);
  145.                 /* now, calculate number of new allocation blocks needed */
  146.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  147.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  148.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  149.                 
  150.                 /* get number of 512-byte blocks needed for resource fork */
  151.                 theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen % 512) ?
  152.                                             (((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1) :
  153.                                             ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9);
  154.                 /* now, calculate number of new allocation blocks needed */
  155.                 theGlobals->allocBlksNeeded += (theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk) ?
  156.                                                 ((theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1) :
  157.                                                 (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk);
  158.             }
  159.         }
  160.         ++index;
  161.     } while ( theGlobals->result == noErr );
  162. }
  163.  
  164. /*****************************************************************************/
  165.  
  166. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  167.                                             long srcDirID,
  168.                                             short dstVRefNum,
  169.                                             Boolean *spaceOK)
  170. {
  171.     HParamBlockRec pb;
  172.     OSErr error;
  173.     unsigned short dstFreeBlocks;
  174.     PreflightGlobals theGlobals;
  175.     
  176.     /* Get the number of 512 byte blocks per allocation block and */
  177.     /* number of free allocation blocks on the destination volume */
  178.     pb.volumeParam.ioVRefNum = dstVRefNum;
  179.     pb.volumeParam.ioNamePtr = NULL;
  180.     pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  181.     error = PBHGetVInfoSync(&pb);
  182.     if ( error == noErr )
  183.     {
  184.         dstFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
  185.         
  186.         /* get allocation block size (always multiple of 512) and divide by 512
  187.           to get number of 512-byte blocks per allocation block */
  188.         theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.volumeParam.ioVAlBlkSiz >> 9);
  189.         
  190.         theGlobals.allocBlksNeeded = 0;
  191.  
  192.         theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
  193.         theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
  194.         
  195.         
  196.         GetLevelSize(srcDirID, &theGlobals);
  197.         
  198.         /* Is there enough room on the destination volume for the source file? */
  199.         *spaceOK = (theGlobals.allocBlksNeeded <= dstFreeBlocks);
  200.     }
  201.     return ( error );
  202. }
  203.  
  204. /*****************************************************************************/
  205.  
  206. static    void    CopyLevel(long sourceDirID,
  207.                           long dstDirID,
  208.                           EnumerateGlobalsPtr theGlobals)
  209. {
  210.     long currentSrcDirID;
  211.     long newDirID;
  212.     short index = 1;
  213.     
  214.     do
  215.     {    /* Isn't C great... What I'd give for a "WITH theGlobals DO" about now... */
  216.     
  217.         /* Get next source item at the current directory level */
  218.         
  219.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  220.         theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
  221.         theGlobals->error = PBGetCatInfoSync((CInfoPBPtr)&theGlobals->myCPB);        
  222.  
  223.         if ( theGlobals->error == noErr )
  224.         {
  225.             /* We have an item.  Is it a file or directory? */
  226.             if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  227.             {
  228.                 /* we have a directory */
  229.                 
  230.                 /* create a new directory at the destination. No errors allowed! */
  231.                 theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
  232.                 
  233.                 if ( theGlobals->error == noErr )
  234.                 {
  235.                     /* Save the current source directory ID where we can get it when we come back
  236.                     ** from recursion land. */
  237.                     
  238.                     currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
  239.                     
  240.                     /* Dive again (copy the directory level we just found below this one) */
  241.                     
  242.                     CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
  243.                     
  244.                     if ( !theGlobals->bailout )
  245.                     {
  246.                         /* Copy comment from old to new directory. */
  247.                         /* Ignore the result because we really don't care if it worked or not. */
  248.                         (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
  249.                     }
  250.  
  251.                     if ( !theGlobals->bailout )
  252.                     {
  253.                         /* Copy directory attributes (dates, etc.) to newDirID. */
  254.                         
  255.                         theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
  256.                         
  257.                         /* handle any errors from CopyFileMgrAttributes */
  258.                         if ( (theGlobals->error != noErr) && (theGlobals->errorHandler != NULL) )
  259.                             theGlobals->bailout =  (* (theGlobals->errorHandler)) (theGlobals->error, copyDirFMAttributesOp,
  260.                                                                                 theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  261.                                                                                 theGlobals->destinationVRefNum, newDirID, NULL);
  262.                     }
  263.                 }
  264.                 else    /* error handling for DirCreate */
  265.                     if ( theGlobals->errorHandler != NULL )
  266.                         theGlobals->bailout = (* (theGlobals->errorHandler)) (theGlobals->error, dirCreateOp,
  267.                                                     theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  268.                                                     theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
  269.                 theGlobals->error = noErr;    /* clear error return on way back */
  270.             }
  271.             else
  272.             {
  273.             
  274.                 /* We have a file, so copy it */
  275.                 
  276.                 theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
  277.                                              theGlobals->myCPB.hFileInfo.ioFlParID,
  278.                                              theGlobals->itemName,
  279.                                              theGlobals->destinationVRefNum,
  280.                                              dstDirID,
  281.                                              NULL,
  282.                                              NULL,
  283.                                              theGlobals->copyBuffer,
  284.                                              theGlobals->bufferSize,
  285.                                              false);
  286.                         
  287.                 /* handle any errors from FileCopy */
  288.                 if ( (theGlobals->error != noErr) && (theGlobals->errorHandler != NULL) )
  289.                     theGlobals->bailout =  (* (theGlobals->errorHandler)) (theGlobals->error, fileCopyOp,
  290.                                                                         theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
  291.                                                                         theGlobals->destinationVRefNum, dstDirID, NULL);
  292.             }
  293.         }
  294.         else
  295.         {    /* error handling for PBGetCatInfo */
  296.             /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
  297.             if ( (theGlobals->error != fnfErr) && (theGlobals->errorHandler != NULL) )
  298.             { 
  299.                 theGlobals->bailout = (* (theGlobals->errorHandler)) (theGlobals->error, getNextItemOp, theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
  300.             }
  301.         }
  302.         
  303.         ++index; /* prepare to get next item */
  304.     } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
  305. }
  306.  
  307. /*****************************************************************************/
  308.  
  309. pascal    OSErr    DirectoryCopy(short srcVRefNum,
  310.                               long srcDirID,
  311.                               StringPtr srcName,
  312.                               short dstVRefNum,
  313.                               long dstDirID,
  314.                               StringPtr dstName,
  315.                               void *copyBufferPtr,
  316.                               long copyBufferSize,
  317.                               Boolean preflight,
  318.                               CopyErrProcPtr copyErrHandler)
  319. {
  320.     EnumerateGlobals theGlobals;
  321.     Boolean    isDirectory;
  322.     OSErr    error;
  323.     Boolean ourCopyBuffer = false;
  324.     Str63    srcDirName;
  325.     Boolean spaceOK;
  326.     
  327.     /* Make sure a copy buffer is allocated. */
  328.     if ( copyBufferPtr == NULL )
  329.     {
  330.         /* The caller didn't supply a copy buffer so grab one from the application heap.
  331.         ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
  332.         ** If 512 bytes aren't available, we're in trouble. */
  333.         copyBufferSize = dirCopyBigCopyBuffSize;
  334.         copyBufferPtr = NewPtr(copyBufferSize);
  335.         if ( copyBufferPtr == NULL )
  336.         {
  337.             copyBufferSize = dirCopyMinCopyBuffSize;
  338.             copyBufferPtr = NewPtr(copyBufferSize);
  339.             if ( copyBufferPtr == NULL )
  340.                 return ( memFullErr );
  341.         }
  342.         ourCopyBuffer = true;
  343.     }
  344.     
  345.     /* Get the real dirID where we're copying from and make sure it is a directory. */
  346.     error = GetDirID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
  347.     if ( error != noErr )
  348.         goto ErrorExit;
  349.     if ( !isDirectory )
  350.     {
  351.         error = dirNFErr;
  352.         goto ErrorExit;
  353.     }
  354.         
  355.     /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
  356.     error = GetDirID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
  357.     if ( error != noErr )
  358.         goto ErrorExit;
  359.     if ( !isDirectory )
  360.     {
  361.         error =  dirNFErr;
  362.         goto ErrorExit;
  363.     }
  364.     
  365.     /* Get the real vRefNum of both the source and destination */
  366.     error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
  367.     if ( error != noErr )
  368.         goto ErrorExit;
  369.     error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
  370.     if ( error != noErr )
  371.         goto ErrorExit;
  372.     
  373.     if ( preflight )
  374.     {
  375.         error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, &spaceOK);
  376.         if ( error != noErr )
  377.             goto ErrorExit;
  378.         if ( !spaceOK )
  379.         {
  380.             error = dskFulErr; /* not enough room on destination */
  381.             goto ErrorExit;
  382.         }
  383.     }
  384.  
  385.     /* Create the new directory in the destination directory with the */
  386.     /* same name as the source directory. */
  387.     error = GetDirName(srcVRefNum, srcDirID, srcDirName);
  388.     if ( error != noErr )
  389.         goto ErrorExit;
  390.     error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
  391.     if ( error != noErr )
  392.         goto ErrorExit;
  393.     
  394.     /* dstDirID is now the newly created directory! */
  395.         
  396.     /* Set up the globals we need to access from the recursive routine. */
  397.     theGlobals.copyBuffer = copyBufferPtr;
  398.     theGlobals.bufferSize = copyBufferSize;
  399.     theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
  400.     theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
  401.     theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
  402.     theGlobals.errorHandler = copyErrHandler;
  403.     theGlobals.bailout = false;
  404.         
  405.     /* Here we go into recursion land... */
  406.     CopyLevel(srcDirID, dstDirID, &theGlobals);
  407.     
  408.     if ( !theGlobals.bailout )
  409.     {
  410.         /* Copy comment from source to destination directory. */
  411.         /* Ignore the result because we really don't care if it worked or not. */
  412.         (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
  413.     }
  414.  
  415.     if ( !theGlobals.bailout )
  416.     {
  417.         /* Copy the File Manager attributes */
  418.         
  419.         error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
  420.                     dstVRefNum, dstDirID, NULL, true);
  421.         
  422.         /* handle any errors from CopyFileMgrAttributes */
  423.         if ( (error != noErr) && (copyErrHandler != NULL) )
  424.             theGlobals.bailout = (* copyErrHandler) (error, copyDirFMAttributesOp,
  425.                                     srcVRefNum, srcDirID, NULL,
  426.                                     dstVRefNum, dstDirID, NULL);
  427.     }
  428.  
  429. ErrorExit:
  430.     /* Get rid of the copy buffer if we allocated it. */
  431.     if ( ourCopyBuffer )
  432.         DisposePtr(copyBufferPtr);
  433.  
  434.     return ( error );
  435. }
  436.  
  437. /*****************************************************************************/
  438.  
  439. pascal    OSErr    FSpDirectoryCopy(const FSSpec *srcSpec,
  440.                                  const FSSpec *dstSpec,
  441.                                  void *copyBufferPtr,
  442.                                  long copyBufferSize,
  443.                                  Boolean preflight,
  444.                                  CopyErrProcPtr copyErrHandler)
  445. {
  446.     return ( DirectoryCopy(srcSpec->vRefNum, srcSpec->parID, (StringPtr)srcSpec->name,
  447.                           dstSpec->vRefNum, dstSpec->parID, (StringPtr)dstSpec->name,
  448.                           copyBufferPtr, copyBufferSize, preflight, copyErrHandler) );
  449. }
  450.  
  451. /*****************************************************************************/
  452.  
  453.